package main

import (
	"bytes"
	"fmt"
	"hash/crc32"
	"os"
	"path"

	"github.com/valyala/fasthttp"

	"strings"

	"github.com/h2non/filetype"
	log "github.com/sirupsen/logrus"
)

func avifMatcher(buf []byte) bool {
	// 0000001C 66747970 6D696631 00000000 6D696631 61766966 6D696166 000000F4
	return len(buf) > 1 && bytes.Equal(buf[:28], []byte{
		0x0, 0x0, 0x0, 0x1c,
		0x66, 0x74, 0x79, 0x70,
		0x6d, 0x69, 0x66, 0x31,
		0x0, 0x0, 0x0, 0x0,
		0x6d, 0x69, 0x66, 0x31,
		0x61, 0x76, 0x69, 0x66,
		0x6d, 0x69, 0x61, 0x66,
	})
}
func getFileContentType(buffer []byte) string {
	var avifType = filetype.NewType("avif", "image/avif")
	filetype.AddMatcher(avifType, avifMatcher)
	kind, _ := filetype.Match(buffer)
	return kind.MIME.Value
}

func imageExists(filename string) bool {
	info, err := os.Stat(filename)
	if os.IsNotExist(err) {
		return false
	}
	log.Debugf("file %s exists!", filename)
	return !info.IsDir()
}

func checkAllowedType(imgFilename string) bool {
	imgFilename = strings.ToLower(imgFilename)
	for _, allowedType := range config.AllowedTypes {
		allowedType = "." + strings.ToLower(allowedType)
		if strings.HasSuffix(imgFilename, allowedType) {
			return true
		}
	}
	return false
}

/**
* 获取文件是否过期
* @param {string} originaFilePath 源文件
* @param {string} filePath 生成的文件
 */
func IsFIleExpire(originaFilePath string, filePath string) bool {
	filePathSTAT, err := os.Stat(filePath)
	if err != nil {
		log.Error(err.Error())
		return true
	}
	originaFilePathSTAT, err := os.Stat(originaFilePath)
	if err != nil {
		log.Error(err.Error())
		return true
	}
	flag := originaFilePathSTAT.ModTime().Unix()-filePathSTAT.ModTime().Unix() > 0
	// 源文件时间减生成的文件时间>0，就过期了
	log.Info("IsFIleExpire: %v", flag)
	if flag { // 删除过期文件
		os.Remove(filePath)
	}
	return flag
}

/**
* 根据文件类型处理文件路径
 */
func genOptimizedAbs(suffix string, exhaustPath string, reqFileURI string, resize string) string {

	outFilename := fmt.Sprintf("%s.%s%s", path.Base(reqFileURI), resize, suffix)

	outWebpFilePath := path.Clean(path.Join(exhaustPath, path.Dir(reqFileURI), outFilename))

	return outWebpFilePath
}

func genEtag(ImgAbsPath string) string {
	data, err := os.ReadFile(ImgAbsPath)
	if err != nil {
		log.Warn(err)
	}
	crc := crc32.ChecksumIEEE(data)
	return fmt.Sprintf(`W/"%d-%08X"`, len(data), crc)
}

/**
* 获取体积对比
 */
// func getCompressionRate(RawImagePath string, optimizedImg string) string {
// 	originFileInfo, err := os.Stat(RawImagePath)
// 	if err != nil {
// 		log.Warnf("Failed to get raw image %v", err)
// 		return ""
// 	}
// 	optimizedFileInfo, err := os.Stat(optimizedImg)
// 	if err != nil {
// 		log.Warnf("Failed to get optimized image %v", err)
// 		return ""
// 	}
// 	compressionRate := float64(optimizedFileInfo.Size()) / float64(originFileInfo.Size())
// 	log.Debugf("The compression rate is %d/%d=%.2f", originFileInfo.Size(), optimizedFileInfo.Size(), compressionRate)
// 	return fmt.Sprintf(`%.2f`, compressionRate)
// }

/**
* 检查浏览器是否支持webp等格式
 */
func guessSupportedFormat(header *fasthttp.RequestHeader) map[string]bool {
	var supported = map[string]bool{
		"raw":  true,
		"webp": false,
		"avif": false,
	}

	var ua = string(header.Peek("user-agent"))
	var accept = strings.ToLower(string(header.Peek("accept")))
	log.Debugf("%s\t%s\n", ua, accept)

	if strings.Contains(accept, "image/webp") {
		supported["webp"] = true
	}
	if strings.Contains(accept, "image/avif") {
		supported["avif"] = true
	}

	// chrome on iOS will not send valid image accept header
	if strings.Contains(ua, "iPhone OS 14") || strings.Contains(ua, "CPU OS 14") ||
		strings.Contains(ua, "iPhone OS 15") || strings.Contains(ua, "CPU OS 15") {
		supported["webp"] = true
	} else if strings.Contains(ua, "Android") || strings.Contains(ua, "Linux") {
		supported["webp"] = true
	}

	return supported
}

/**
* 与源文件对比返回最小的文件路径
 */
func findSmallestFiles(files []string) string {
	// walk files
	var small int64
	var final string
	for _, f := range files {
		stat, err := os.Stat(f)
		if err != nil {
			log.Warnf("%s 文件没找到", f)
			continue
		}
		if stat.Size() < small || small == 0 {
			small = stat.Size()
			final = f
		}
	}
	return final
}
